﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;
namespace Jtype1
{
    class Control
    {
        //list of tasks
        private List<Task> taskList;
        //history of commands
        private List<Command> commandList;

        private DataStorage dataStorage;
        /// <summary>
        /// constructor of Control and create objects of taskList, commandList, errorList and dataStorage.
        /// </summary>
        public Control()
        {
            taskList = new List<Task>{};
            commandList = new List<Command>{};
            dataStorage = new DataStorage();
        }
        /// <summary>
        /// read the command from user
        /// </summary>
        /// <returns>the unformated command user input</returns>
        public string ReadCommand()
        {
            Console.WriteLine("Command:");
            string userCommand=Console.ReadLine();

            return userCommand;
        }
        /// <summary>
        /// sort the task list accoring to task deadline. If two tasks have the same deadline and it will sort by task name
        /// </summary>
        public void SortDeadline()
        {
            taskList.Sort(delegate (Task t1, Task t2)
            {
                if (t1.Deadline.CompareTo(t2.Deadline) != 0)
                    return t1.Deadline.CompareTo(t2.Deadline);
                else
                    return t1.Name.CompareTo(t2.Name);
            });
        }
        /// <summary>
        /// pass the user command to commandpaser to get a formated command
        /// </summary>
        /// <param name="userCommand">command user inputs</param>
        /// <returns>the formated command after paserting</returns>
        public Command AnalyseCommand(string userCommand)
        {
            //parse the command and store information in a command object
            CommandParser parser = new CommandParser(userCommand);
            Command command = parser.GetCommand();

            return command;
        }
        /// <summary>
        /// Doing operation according to command type
        /// </summary>
        /// <param name="command">targat command</param>
        /// <returns>the right command is operated successfully</returns>
        public bool ProcessCommand(Command command)
        {
            bool finish = true;

            switch (command.Type)
            {
                case "add":
                    finish = AddTask(command);
                    break;
                case "delete":
                    finish = DeleteTask(command);
                    break;
                case "search":
                    Display(SearchTask(command));
                    break;
                case "edit": 
                    finish = Edit(command);
                    break;
                case "clear":
                    ClearTask();
                    break;
                case "home":
                    DisplayAll();
                    break;
                case "done":
                    finish = DoneTask(command);
                    break;
                default:
                    InvalidCommand();
                    break;
            }

            return finish;
        }
        /// <summary>
        /// set the status of task is done already
        /// </summary>
        /// <param name="command">targat command</param>
        /// <returns>whether done task is successful</returns>
        private bool DoneTask(Command command)
        {
            if (command.TaskKey.Count == 0)
            {
                if (command.TaskIndex.Count == 0)
                {
                    Console.WriteLine(Error.DoneError.NO_INDEX);
                    return false;
                }
                for (int i = 0; i < command.TaskIndex.Count; i++)
                {
                    if (command.TaskIndex[i] >= taskList.Count || command.TaskIndex[i] < 0)
                    {
                        Console.WriteLine(Error.DoneError.INDEX_OVER_FLOW);
                        return false;
                    } 
                }

                for (int i = 0; i < command.TaskIndex.Count; i++)
                {
                    taskList[command.TaskIndex[i]].Status = 1;
                }

                SortDeadline();
                DisplayAll();
                return true;
            }
            else
            {
                List<int> findResult = new List<int> { };
                findResult = SearchTask(command);
                Display(findResult);
                return true;
            }

        }
        /// <summary>
        /// print the error message of invalid commmand
        /// </summary>
        private void InvalidCommand()
        {
            Console.WriteLine(Error.CommandError.INVALID_COMMAND);
        }
        /// <summary>
        /// add a task in list
        /// </summary>
        /// <param name="command">targat command</param>
        /// <returns>whether addition is successful</returns>
        private bool AddTask(Command command)
        {
            Task newTask = new Task();
            bool finish;

            if (command.TaskDeadline == DateTime.MinValue)
                newTask.Deadline = DateTime.Today.Date;
            else
                newTask.Deadline = command.TaskDeadline;
            
            newTask.Location = command.TaskLocation;
            
            if(command.TaskName == "")
            {
                finish = false;
                Console.WriteLine(Error.AddError.NO_TASK_NAME);
            }
            else
            {
                newTask.Name = command.TaskName;
                taskList.Add(newTask);
                SortDeadline();
                checkStatus();
                DisplayAll();
                finish = true;
            }

            return finish;
        }
        /// <summary>
        /// Delete a task(tasks) in the list. If user delete by index, it can delete directly.
        /// If user delete by taskKey, it will show the tasks related  to the taskKey and 
        /// let user choose which task should be deleted.
        /// </summary>
        /// <param name="command">targat command</param>
        /// <returns>whether deletion is successful</returns>
        private bool DeleteTask(Command command)
        {
            if (command.TaskKey.Count==0)
            {
                if (command.TaskIndex.Count == 0)
                {
                    Console.WriteLine(Error.DeleteError.NO_INDEX);
                    return false;
                }
                for (int i = 0; i < command.TaskIndex.Count; i++)
                {
                    if (command.TaskIndex[i] >= taskList.Count || command.TaskIndex[i] < 0)
                    {
                        Console.WriteLine(Error.DeleteError.INDEX_OVER_FLOW);
                        return false;
                    }   
                }
                //sort index by descending order, delete repeated index
                command.TaskIndex.Sort();
                command.TaskIndex.Reverse();
                List<int> deleteIndex = new List<int>{};
                for (int i = 0; i < command.TaskIndex.Count; i++)
                {
                    if (!deleteIndex.Contains(command.TaskIndex[i]))
                        deleteIndex.Add(command.TaskIndex[i]);
                }
                for (int i = 0; i < deleteIndex.Count; i++)
                {
                    taskList.RemoveAt(deleteIndex[i]);
                }

                SortDeadline();
                checkStatus();
                DisplayAll();
                return true;
            }
            else
            {
                List<int> findResult = new List<int> { };
                findResult = SearchTask(command);
                Display(findResult);
                return true;
            }
        }
        /// <summary>
        /// edit a task in the list
        /// </summary>
        /// <param name="command">the targat command</param>
        /// <returns>whether edit successfully</returns>
        private bool Edit(Command command)
        {
            if (command.TaskIndex.Count == 0 && command.TaskKey.Count == 0)
            {
                Console.WriteLine(Error.EditError.NO_INDEX);
                return false;
            }
            else
            {
                if (command.TaskIndex.Count == 1)
                {
                    if (command.TaskIndex[0] >= taskList.Count || command.TaskIndex[0] < 0)
                    {
                        Console.WriteLine(Error.EditError.INDEX_OVER_FLOW);
                        return false;
                    }

                    if (command.TaskDeadline != DateTime.MinValue)
                        taskList[command.TaskIndex[0]].Deadline = command.TaskDeadline;//exception handling here

                    if (command.TaskLocation != null)
                        taskList[command.TaskIndex[0]].Location = command.TaskLocation;

                    // these three functions can be grouped together
                    SortDeadline();
                    checkStatus();
                    DisplayAll();

                    return true;
                }
                else
                {
                    List<int> findResult = new List<int> { };
                    findResult = SearchTask(command);
                    Display(findResult);
                    return true;
                }
            }
        }
        /// <summary>
        /// the search tasks in list which related to input taskKey in command. Also it can search by some deadline like “today”, “Mon”, etc. 
        /// </summary>
        /// <param name="command">targat command for searching</param>
        /// <returns>the list of task which follow the sesarch condition</returns>
        private List<int> SearchTask(Command command)
        {
            bool match;
            List<int> result = new List<int> { };
            List<string> keyWords = new List<string> { };

            foreach(string str in command.TaskKey)
            {
                keyWords.Add(str.ToUpper());
            }

            for (int i = 0; i < taskList.Count; i++)
            {
                match = true;
                string singleTask="";
                singleTask+=taskList[i].Name.ToUpper()+" ";

                if (taskList[i].Location != null)
                {
                    singleTask += taskList[i].Location.ToUpper() + " ";
                }

                if (taskList[i].Description != null)
                {
                    singleTask += taskList[i].Description.ToUpper() + " ";
                }

                if (taskList[i].Deadline.Date == DateTime.Today.Date)
                {
                    singleTask += "TODAY";
                }

                singleTask+=taskList[i].Deadline.ToString("D").ToUpper()+" ";

                for (int j = 0; j < keyWords.Count; j++)
                {
                    if (singleTask.IndexOf(keyWords[j])==-1)
                    {
                        match = false;
                        break;
                    }
                }
                if (match) result.Add(i);
            }
            return result;
        }
        /// <summary>
        /// delete all task
        /// </summary>
        private void ClearTask()
        {
            taskList.Clear();
            DisplayAll();
        }
        /// <summary>
        /// print out a list of task information
        /// </summary>
        /// <param name="indexs"></param>
        private void Display(List<int> indexs)
        {
            Console.Clear();

            if(indexs.Count == 0)
            {
                Console.WriteLine("No result found!");
            }
            else
            {
            for (int i = 0; i < indexs.Count; i++)
                {
                    Console.WriteLine( indexs[i]+1+" ");
                    Console.WriteLine("Event name: "+ taskList[indexs[i]].Name);
                    Console.WriteLine("Location: " + taskList[indexs[i]].Location);
                    //Console.WriteLine("Description: " + taskList[indexs[i]].Description);
                    Console.WriteLine("Deadline: " + taskList[indexs[i]].Deadline.ToString("D"));
                    //Console.WriteLine("Priority: " + taskList[indexs[i]].Priority);
                    ShowStatus(taskList[indexs[i]]);
                }
            }
        }
        /// <summary>
        /// print out all information of all tasks
        /// </summary>
        private void DisplayAll()
        {
            Console.Clear();

            for (int i = 0; i < taskList.Count; i++)
            {
                Console.WriteLine(i+1+" ");
                Console.WriteLine("Event name: " + taskList[i].Name);
                Console.WriteLine("Location: " + taskList[i].Location);
                //Console.WriteLine("Description: " + taskList[i].Description);
                Console.WriteLine("Deadline: " + taskList[i].Deadline.ToString("D"));
                //Console.WriteLine("Priority: " + taskList[i].Priority);
                ShowStatus(taskList[i]);

            }
        }
        /// <summary>
        /// print out the status of a task
        /// </summary>
        /// <param name="todo">the task</param>
        private void ShowStatus(Task todo)
        {
            switch (todo.Status)
            {
                case 0:
                    Console.WriteLine("Status: Scheduled");
                    break;
                case 1:
                    Console.WriteLine("Status: Done");
                    break;
                case 2:
                    Console.WriteLine("Status: Coming Soon");
                    break;
                case 3:
                    Console.WriteLine("Status: OVERDUE");
                    break;
            }
        }
        /// <summary>
        /// feedback whether a command has done successfully or not
        /// </summary>
        /// <param name="finish">the flag of whether a task is done</param>
        /// <param name="userCommand">command user input</param>
        /// <param name="command">command after pasering</param>
        private void feedback(bool finish, string userCommand, Command command)
        {
            //if (finish == true)
            //{
            //    Console.WriteLine(command.TaskName + " is successful!");
            //}
            //else
            if(!finish)
            {
                Console.WriteLine(command.Type + " is NOT successful!");
                Console.WriteLine("Please review your command:");
                Console.WriteLine("\t" + userCommand);
            }
        }
        /// <summary>
        /// check the task is done or on schedule
        /// </summary>
        private void checkStatus()
        {
            foreach (Task todo in taskList)
            {
                if (todo.Status != 1)
                {
                    if (todo.Deadline.Date < DateTime.Now.Date)
                    {
                        todo.Status = 3;
                        continue;
                    }

                    if (todo.Deadline.Date == DateTime.Today.Date)
                    {
                        todo.Status = 2;
                        continue;
                    }
                }
            }
        }
        /// <summary>
        /// the main loop in control which read the command and do operation
        /// </summary>
        public void Process()
        {       
            bool finish;

            taskList = dataStorage.readDataFromXMLFile("data");
            if(taskList == null)
                taskList = new List<Task> { };
            checkStatus();
            DisplayAll();

            string userCommand = ReadCommand();
            while (userCommand != "exit")
            {
                Command command = AnalyseCommand(userCommand);
                finish = ProcessCommand(command);
                feedback(finish, userCommand, command);
                userCommand = ReadCommand();
            }

            dataStorage.writeDataToFile("data", taskList);
        }

    }
}
